package edu.northwestern.cbits.purple_robot_manager.plugins; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.ActivityManager.RunningTaskInfo; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.location.Location; import android.net.wifi.ScanResult; import android.os.BadParcelableException; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; public abstract class OutputPlugin { private final ArrayList<Intent> _pendingIntents = new ArrayList<>(); private boolean _isProcessing = false; public static final String PAYLOAD = "edu.northwestern.cbits.purple_robot.OUTPUT_EVENT_PLUGIN"; public static final String OUTPUT_EVENT = "edu.northwestern.cbits.purple_robot.OUTPUT_EVENT"; public static final String LOG_EVENT = "edu.northwestern.cbits.purple_robot.LOG_EVENT"; public static final String FORCE_UPLOAD = "edu.northwestern.cbits.purple_robot.FORCE_UPLOAD"; public static final String DISPLAY_MESSAGE = "edu.northwestern.cbits.purple_robot.DISPLAY_MESSAGE"; public static final String USE_EXTERNAL_STORAGE = "config_external_storage"; private static List<Class<OutputPlugin>> _pluginClasses = new ArrayList<>(); public abstract String[] respondsTo(); public abstract void processIntent(Intent intent); private Context _context = null; public void setContext(Context context) { this._context = context; } public Context getContext() { return this._context; } protected void broadcastMessage(String message, boolean log) { Intent displayIntent = new Intent(OutputPlugin.DISPLAY_MESSAGE); displayIntent.putExtra("MESSAGE", message); LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this.getContext()); manager.sendBroadcast(displayIntent); if (log) { HashMap<String, Object> payload = new HashMap<>(); payload.put("message", message); LogManager.getInstance(this.getContext()).log("broadcast_message", payload); } } public boolean shouldRespond(String intentAction) { String[] actions = this.respondsTo(); for (String action : actions) { if (action.equalsIgnoreCase(intentAction)) return true; } return false; } @SuppressWarnings("unchecked") public static void loadPluginClasses(Context context) { String packageName = OutputPlugin.class.getPackage().getName(); String[] probeClasses = context.getResources().getStringArray(R.array.output_plugin_classes); IntentFilter intentFilter = new IntentFilter(); for (String className : probeClasses) { try { @SuppressWarnings("rawtypes") Class pluginClass = Class.forName(packageName + "." + className); OutputPlugin.registerPluginClass(pluginClass); OutputPlugin plugin = (OutputPlugin) pluginClass.newInstance(); Method method = pluginClass.getDeclaredMethod("respondsTo"); String[] actions = (String[]) method.invoke(plugin); for (String action : actions) { if (intentFilter.hasAction(action) == false) intentFilter.addAction(action); } } catch (ClassNotFoundException | InstantiationException | InvocationTargetException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException e) { LogManager.getInstance(context).logException(e); } } intentFilter.addAction(OutputPlugin.OUTPUT_EVENT); LocalBroadcastManager localManager = LocalBroadcastManager.getInstance(context); localManager.registerReceiver(OutputPluginManager.sharedInstance, intentFilter); } @SuppressWarnings("rawtypes") public static void registerPluginClass(Class<OutputPlugin> pluginClass) { if (!OutputPlugin._pluginClasses.contains(pluginClass)) OutputPlugin._pluginClasses.add(pluginClass); } @SuppressWarnings("rawtypes") public static List<Class<OutputPlugin>> availablePluginClasses() { return OutputPlugin._pluginClasses; } public void process(final Intent intent) { if (this.shouldRespond(intent.getAction())) { synchronized (this._pendingIntents) { this._pendingIntents.add(intent); } if (this._isProcessing == false) { final OutputPlugin me = this; Runnable r = new Runnable() { public void run() { me._isProcessing = true; while (me._pendingIntents.size() > 0) { Intent nextIntent = null; synchronized (me._pendingIntents) { if (me._pendingIntents.size() > 0) nextIntent = me._pendingIntents.remove(0); } if (nextIntent != null) me.processIntent(nextIntent); } me._isProcessing = false; } }; try { Thread t = new Thread(r); t.start(); } catch (OutOfMemoryError e) { LogManager.getInstance(this._context).logException(e); } } } } public static Map<String, Object> getValues(final Bundle bundle) { HashMap<String, Object> values = new HashMap<>(); if (bundle == null) return values; try { for (String key : bundle.keySet()) { values.put(key, bundle.get(key)); } } catch (BadParcelableException e) { e.printStackTrace(); } return values; } public static JSONObject jsonForBundle(Bundle bundle) throws JSONException { JSONObject json = new JSONObject(); Map<String, Object> values = OutputPlugin.getValues(bundle); for (String key : values.keySet()) { Object value = values.get(key); if (value == null || key == null) { // Skip } else if (value instanceof String) json.put(key, value); else if (value instanceof Bundle) { value = OutputPlugin.jsonForBundle((Bundle) value); json.put(key, value); } else if (value instanceof float[]) { float[] floats = (float[]) value; JSONArray floatArray = new JSONArray(); for (float f : floats) { floatArray.put((double) f); } json.put(key, floatArray); } else if (value instanceof int[]) { int[] ints = (int[]) value; JSONArray intArray = new JSONArray(); for (int i : ints) { intArray.put(i); } json.put(key, intArray); } else if (value instanceof long[]) { long[] longs = (long[]) value; JSONArray longArray = new JSONArray(); for (long l : longs) { longArray.put(l); } json.put(key, longArray); } else if (value instanceof double[]) { double[] doubles = (double[]) value; JSONArray doubleArray = new JSONArray(); for (double d : doubles) { doubleArray.put(d); } json.put(key, doubleArray); } else if (value instanceof Float) { Float f = (Float) value; json.put(key, f.doubleValue()); } else if (value instanceof Integer) { Integer i = (Integer) value; json.put(key, i.intValue()); } else if (value instanceof Long) { Long l = (Long) value; json.put(key, l.longValue()); } else if (value instanceof Boolean) { Boolean b = (Boolean) value; json.put(key, b.booleanValue()); } else if (value instanceof Short) { Short s = (Short) value; json.put(key, s.intValue()); } else if (value instanceof Double) { Double d = (Double) value; if (d.isInfinite()) json.put(key, Double.MAX_VALUE); else json.put(key, d.doubleValue()); } else if (value instanceof List) { @SuppressWarnings("unchecked") List<Object> list = (List<Object>) value; JSONArray objectArray = new JSONArray(); for (Object o : list) { if (o instanceof Bundle) objectArray.put(OutputPlugin.jsonForBundle((Bundle) o)); else if (o instanceof ScanResult) { ScanResult s = (ScanResult) o; JSONObject scanObject = new JSONObject(); if (s.BSSID != null) scanObject.put("BSSID", s.BSSID); if (s.SSID != null) scanObject.put("SSID", s.SSID); if (s.capabilities != null) scanObject.put("Capabilities", s.capabilities); scanObject.put("Frequency", s.frequency); scanObject.put("Level dBm", s.level); objectArray.put(scanObject); } else if (o instanceof RunningTaskInfo) { RunningTaskInfo r = (RunningTaskInfo) o; JSONObject taskObject = new JSONObject(); if (r.baseActivity != null) taskObject.put("Base Activity", r.baseActivity.getPackageName()); if (r.description != null) taskObject.put("Description", r.description.toString()); taskObject.put("Activity Count", r.numActivities); taskObject.put("Running Activity Count", r.numRunning); objectArray.put(taskObject); } else if (o instanceof ApplicationInfo) { ApplicationInfo a = (ApplicationInfo) o; objectArray.put(a.packageName); } else if (o instanceof Location) { Location l = (Location) o; JSONObject locObject = new JSONObject(); locObject.put("Accuracy", l.getAccuracy()); locObject.put("Altitude", l.getAltitude()); locObject.put("Bearing", l.getBearing()); locObject.put("Latitude", l.getLatitude()); locObject.put("Longitude", l.getLongitude()); if (l.getProvider() != null) locObject.put("Provider", l.getProvider()); else locObject.put("Provider", "Unknown"); locObject.put("Speed", l.getSpeed()); locObject.put("Timestamp", l.getTime()); objectArray.put(locObject); } else if (o instanceof String) objectArray.put(o.toString()); else Log.e("PR", "LIST OBJ: " + o.getClass().getCanonicalName() + " IN " + key); } json.put(key, objectArray); } else if (value instanceof Location) { Location l = (Location) value; JSONObject locObject = new JSONObject(); locObject.put("Accuracy", l.getAccuracy()); locObject.put("Altitude", l.getAltitude()); locObject.put("Bearing", l.getBearing()); locObject.put("Latitude", l.getLatitude()); locObject.put("Longitude", l.getLongitude()); if (l.getProvider() != null) locObject.put("Provider", l.getProvider()); else locObject.put("Provider", "Unknown"); locObject.put("Speed", l.getSpeed()); locObject.put("Timestamp", l.getTime()); json.put(key, locObject); } else if (value instanceof BluetoothClass) { BluetoothClass btClass = (BluetoothClass) value; json.put(key, btClass.toString()); } else if (value instanceof BluetoothDevice) { BluetoothDevice device = (BluetoothDevice) value; JSONObject deviceObject = new JSONObject(); if (device.getBondState() == BluetoothDevice.BOND_BONDED) deviceObject.put("Bond State", "Bonded"); if (device.getBondState() == BluetoothDevice.BOND_BONDING) deviceObject.put("Bond State", "Bonding"); else deviceObject.put("Bond State", "None"); deviceObject.put("Device Address", device.getAddress()); deviceObject.put("Device Class", device.getBluetoothClass().toString()); json.put(key, deviceObject); } else Log.e("PRM", "GOT TYPE " + value.getClass().getCanonicalName() + " FOR " + key); } // String jsonString = JsonUtils.getGson().toJson(values); return json; } @SuppressWarnings("rawtypes") public static Bundle bundleForJson(final Context context, String jsonString) { Bundle bundle = new Bundle(); try { JSONObject json = new JSONObject(jsonString); Iterator keys = json.keys(); while (keys.hasNext()) { String key = keys.next().toString(); Object o = json.get(key); if (o instanceof Double) bundle.putDouble(key, (Double) o); else if (o instanceof Float) bundle.putDouble(key, ((Float) o).doubleValue()); else if (o instanceof Long) bundle.putDouble(key, ((Long) o).doubleValue()); else if (o instanceof Integer) bundle.putDouble(key, ((Integer) o).doubleValue()); else if (o instanceof Boolean) bundle.putBoolean(key, (Boolean) o); else if (o instanceof String) bundle.putString(key, o.toString()); else if (o instanceof JSONArray) { JSONArray child = (JSONArray) o; if (child.length() > 0) { Object nextChild = child.get(0); if (nextChild instanceof String) { String[] strings = new String[child.length()]; for (int i = 0; i < strings.length; i++) strings[i] = child.getString(i); bundle.putStringArray(key, strings); } else if (nextChild instanceof Double) { double[] doubles = new double[child.length()]; for (int i = 0; i < doubles.length; i++) doubles[i] = child.getDouble(i); bundle.putDoubleArray(key, doubles); } else if (nextChild instanceof Integer) { double[] doubles = new double[child.length()]; for (int i = 0; i < doubles.length; i++) doubles[i] = child.getDouble(i); bundle.putDoubleArray(key, doubles); } else if (nextChild instanceof Long) { double[] doubles = new double[child.length()]; for (int i = 0; i < doubles.length; i++) doubles[i] = child.getDouble(i); bundle.putDoubleArray(key, doubles); } else if (nextChild instanceof JSONObject) { ArrayList<Bundle> bundles = new ArrayList<>(); for (int i = 0; i < child.length(); i++) bundles.add(OutputPlugin.bundleForJson(context, child.getJSONObject(i).toString())); bundle.putParcelableArrayList(key, bundles); } } } else if (o instanceof JSONObject) { JSONObject child = (JSONObject) o; bundle.putBundle(key, OutputPlugin.bundleForJson(context, child.toString())); } } } catch (JSONException e) { LogManager.getInstance(context).logException(e); } return bundle; } }